#pragma once

struct TestRunTransData
{
	CAtlStringW wstrName;
	BOOL		bRun;
	DWORD		lParam;
	
	TestRunTransData() : bRun(TRUE), lParam(0) {};
	TestRunTransData(const TestRunTransData & r) {	wstrName = r.wstrName;	};
	TestRunTransData & operator=(const TestRunTransData & r) { if (this != &r) wstrName = r.wstrName; return *this; }

	//returns size of buffer needed in byte to store all data
	inline DWORD DefineSize() const
	{
		return sizeof(lParam) + sizeof(bRun) + sizeof(DWORD) + wstrName.GetLength() * 2;
	};
	
	//Serializes binary data into buffer given in lpBuf that has size nSize bytes
	//returns the pointer next to serialized item if succeeded or NULL otherwise
	LPBYTE Serialize(IN OUT LPBYTE lpBuf, IN DWORD nSize) const;
	
	//Deserializes binary data and initialize class member content
	//returns the pointer next to the deserialized item or NULL in case of error
	LPBYTE DeSerialize(IN LPBYTE lpBuf, DWORD nLength);
};

struct TestGroupTransData
{
	CAtlStringW wstrName;
	BOOL		bRun;
	DWORD		lParam;
	
	CAtlArray<TestRunTransData> aTestRuns;

	TestGroupTransData() : bRun(TRUE), lParam(0) {};
	TestGroupTransData(const TestGroupTransData & r) {	wstrName = r.wstrName; aTestRuns.Copy(r.aTestRuns); }
	TestGroupTransData & operator=(const TestGroupTransData & r)
	{
		if (this != &r)
		{
			wstrName = r.wstrName;
			aTestRuns.Copy(r.aTestRuns);
		}
		return *this;
	}

	//returns size of buffer needed in byte to store all data
	DWORD DefineSize() const;
	
	//Serializes binary data into buffer given in lpBuf that has size nSize bytes
	//returns the pointer next to serialized item if succeeded or NULL otherwise
	LPBYTE Serialize(IN OUT LPBYTE lpBuf, IN DWORD nSize) const;

	//Deserializes binary data and initialize class member content
	//returns the pointer next to the deserialized item or NULL in case of error
	LPBYTE DeSerialize(IN LPBYTE lpBuf, DWORD nLength);
};

struct TestPackTransData
{
	CAtlArray<TestGroupTransData> aTestGroups;

	TestPackTransData() {};
	TestPackTransData(const TestPackTransData & r) { aTestGroups.Copy(r.aTestGroups); }

	//returns size of buffer needed in byte to store all data
	DWORD DefineSize() const;
	
	//Serializes binary data into buffer given in lpBuf that has size nSize bytes
	//returns the pointer next to serialized item if succeeded or NULL otherwise
	LPBYTE Serialize(IN OUT LPBYTE lpBuf, IN DWORD nSize) const;

	//Deserializes binary data and initialize class member content
	//returns the pointer next to the deserialized item or NULL in case of error
	LPBYTE DeSerialize(IN LPBYTE lpBuf, DWORD nLength);
};


//Serializes binary data into buffer given in lpBuf that has size nSize bytes
//returns the pointer next to serialized item if succeeded or NULL otherwise
inline LPBYTE TestRunTransData::Serialize(IN OUT LPBYTE lpBuf, IN DWORD nSize) const
{
	if (nSize >= DefineSize())
	{
		LPBYTE p = lpBuf;
		*((DWORD*)p) = wstrName.GetLength(); p += sizeof(DWORD);
		errno_t err = memcpy_s(p, (nSize - (p - lpBuf)), wstrName.GetString(), wstrName.GetLength() * sizeof(wchar_t));
		if (err != 0) 
			return NULL;
		p += wstrName.GetLength() * sizeof(wchar_t);
		*((BOOL*)p) = bRun; p += sizeof(bRun);
		*((DWORD*)p) = lParam; p += sizeof(lParam);

		return p;
	}
	return NULL;
}

//Deserializes binary data and initialize class member content
//returns the pointer next to the deserialized item or NULL in case of error
inline LPBYTE TestRunTransData::DeSerialize(IN LPBYTE lpBuf, DWORD nLength)
{
	//reset all
	wstrName.Empty();
	
	if ((lpBuf) &&
		(nLength >= sizeof(DWORD) + sizeof(bRun) + sizeof(lParam)))
	{
		LPBYTE p = lpBuf;
		DWORD nWCharLen = *((DWORD*)p); p += sizeof(DWORD);
		DWORD nLen = nWCharLen * sizeof(wchar_t);
		if (nLen > nLength - (p - lpBuf))
			return NULL;
		errno_t err = memcpy_s(wstrName.GetBuffer(nWCharLen + 1), nLen + sizeof(wchar_t), p, nLen);
		wstrName.ReleaseBuffer(nWCharLen);
		if (err != 0)
			return NULL;
		p += nLen;
		bRun = *((BOOL*)p); p += sizeof(bRun);
		lParam = *((DWORD*)p); p += sizeof(lParam);
		
		return p;
	}

	return NULL;
}


//returns size of buffer needed in byte to store all data
inline DWORD TestGroupTransData::DefineSize() const
{
	DWORD nRet = sizeof(lParam) + sizeof(bRun) +
		sizeof(DWORD) + wstrName.GetLength() * 2 +
		sizeof(DWORD);

	for (size_t n = 0; n < aTestRuns.GetCount(); ++n)
		nRet += aTestRuns.GetAt(n).DefineSize();

	return nRet;
}

//Serializes binary data into buffer given in lpBuf that has size nSize bytes
//returns the pointer next to serialized item if succeeded or NULL otherwise
inline LPBYTE TestGroupTransData::Serialize(IN OUT LPBYTE lpBuf, IN DWORD nSize) const
{
	if (nSize >= DefineSize())
	{
		LPBYTE pEnd = lpBuf + nSize;
		LPBYTE p = lpBuf;
		*((DWORD*)p) = wstrName.GetLength(); p += sizeof(DWORD);
		errno_t err = memcpy_s(p, (nSize - (p - lpBuf)), wstrName.GetString(), wstrName.GetLength() * sizeof(wchar_t));
		if (err != 0) 
			return NULL;
		p += wstrName.GetLength() * sizeof(wchar_t);
		*((BOOL*)p) = bRun; p += sizeof(bRun);
		*((DWORD*)p) = lParam; p += sizeof(lParam);
		
		*((DWORD*)p) = static_cast<DWORD>(aTestRuns.GetCount()); p += sizeof(DWORD);
		for (size_t n = 0; (n < aTestRuns.GetCount()) && (p != NULL); ++n)
		{
			p = aTestRuns.GetAt(n).Serialize(p, static_cast<DWORD>(pEnd - p));
		}

		return p;
	}
	return NULL;
}

//Deserializes binary data and initialize class member content
//returns the pointer next to the deserialized item or NULL in case of error
inline LPBYTE TestGroupTransData::DeSerialize(IN LPBYTE lpBuf, DWORD nLength)
{
	//reset all
	wstrName.Empty();
	aTestRuns.RemoveAll();
	
	if ((lpBuf) &&
		(nLength >= sizeof(DWORD) + sizeof(DWORD) + sizeof(lParam) + sizeof(bRun)))
	{
		LPBYTE pEnd = lpBuf + nLength;
		LPBYTE p = lpBuf;
		DWORD nWCharLen = *((DWORD*)p); p += sizeof(DWORD);
		DWORD nLen = nWCharLen * sizeof(wchar_t);
		if (nLen > nLength - (p - lpBuf))
			return NULL;
		errno_t err = memcpy_s(wstrName.GetBuffer(nWCharLen + 1), nLen + sizeof(wchar_t), p, nLen);
		wstrName.ReleaseBuffer(nWCharLen);
		if (err != 0)
			return NULL;
		p += nLen;
		bRun = *((BOOL*)p); p += sizeof(bRun);
		lParam = *((DWORD*)p); p += sizeof(lParam);
		
		size_t nCount = *((DWORD*)p); p += sizeof(DWORD);
		for (size_t n = 0; (n < nCount) && (p != NULL); ++n)
		{
			p = aTestRuns.GetAt(aTestRuns.Add()).DeSerialize(p, static_cast<DWORD>(pEnd - p));
		}

		return p;
	}

	return NULL;
}

//returns size of buffer needed in byte to store all data
inline DWORD TestPackTransData::DefineSize() const
{
	DWORD nSize = sizeof(DWORD);

	for (size_t n = 0; n < aTestGroups.GetCount(); ++n)
		nSize += aTestGroups.GetAt(n).DefineSize();

	return nSize;
}

//Serializes binary data into buffer given in lpBuf that has size nSize bytes
//returns the pointer next to serialized item if succeeded or NULL otherwise
inline LPBYTE TestPackTransData::Serialize(IN OUT LPBYTE lpBuf, IN DWORD nSize) const
{
	if (nSize >= DefineSize())
	{
		LPBYTE pEnd = lpBuf + nSize;
		LPBYTE p = lpBuf;
				
		*((DWORD*)p) = static_cast<DWORD>(aTestGroups.GetCount()); p += sizeof(DWORD);
		for (size_t n = 0; (n < aTestGroups.GetCount()) && (p != NULL); ++n)
		{
			p = aTestGroups.GetAt(n).Serialize(p, static_cast<DWORD>(pEnd - p));
		}

		return p;
	}
	return NULL;
}

//Deserializes binary data and initialize class member content
//returns the pointer next to the deserialized item or NULL in case of error
inline LPBYTE TestPackTransData::DeSerialize(IN LPBYTE lpBuf, DWORD nLength)
{
	aTestGroups.RemoveAll();

	if ((lpBuf) &&
		(nLength >= sizeof(DWORD)))
	{
		LPBYTE pEnd = lpBuf + nLength;
		LPBYTE p = lpBuf;
		size_t nCount = *((DWORD*)p); p += sizeof(DWORD);
		for (size_t n = 0; (n < nCount) && (p != NULL); ++n)
		{
			p = aTestGroups.GetAt(aTestGroups.Add()).DeSerialize(p, static_cast<DWORD>(pEnd - p));
		}

		return p;
	}

	return NULL;
}
